#!/usr/bin/env python
"""
Acorn-SDR (A Collaboration of Radio Nodes)
(c) Bob Cowdery (G3UKB) 2009

File: dttsp_tx_service.py
Date: July 2009

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

The author can be reached by email at

bob@g3ukb.co.uk

================================================================================
Module Description:

The dttsp service for the transmit path.

"""

#-------------------------------------------------------------------------------
# System imports
import sys
import os
from Numeric import *
from time import sleep
from scipy import signal
import Ice, IceStorm

# Application imports
import libdttsp
import ice_helpers
import utils
import dttsp_tx
from dttsp_map import *

# Import the Ice interface definitions
import AcornLifeCycle
import AcornTxUpdate
import AcornTxDsp
import AcornMicIn
import AcornIqOut
import AcornPersist
import AcornTxDisplays

#===============================================================================
# Ice life-cycle events for the GUI
class LifeCycleI(AcornLifeCycle.LifeCycle):
    
    #---------------------------------------------------------------------------   
    def __init__(self, dttsp, service):
        self.dttsp = dttsp
        self.service = service
        self.dttsp.start()
      
    #---------------------------------------------------------------------------
    def start(self, current=None):
        # For now don't start/stop here as the restart is faulty
        # It works fine to leave the dsp running from init to terminate
        #self.dttsp.start()
        pass
     
    #---------------------------------------------------------------------------   
    def stop(self, current=None):
        #self.dttsp.stop()
        pass
    
    #---------------------------------------------------------------------------    
    def terminate(self, current=None):
        self.dttsp.stop()
        self.service.terminate()
        
#===============================================================================
# Commands for the DSP
class TxDspI(AcornTxDsp.TxDsp):
    
    #---------------------------------------------------------------------------   
    def __init__(self, dttsp, service):
        self.dttsp = dttsp
        self.service = service
        self.filter = '2K1'
        self.mode = 'LSB'
        
    #---------------------------------------------------------------------------
    def setMode(self, mode, current=None):
        self.dttsp.mode(mode)
    
    #---------------------------------------------------------------------------    
    def setFilter(self, filter, current=None):
        self.dttsp.filter(filter)
    
    #---------------------------------------------------------------------------    
    def oscSet(self, offset, current=None):
        self.dttsp.osc_set(offset)
        
    #---------------------------------------------------------------------------    
    def oscInc(self, inc, current=None):
        self.dttsp.osc_inc(inc) 

#===============================================================================
# Ice config events for the DSP
class TxConfigI(AcornTxUpdate.TxConfig):
    
    #---------------------------------------------------------------------------   
    def __init__(self, dttsp, persist, config_map):
        self.dttsp = dttsp
        self.persist = persist
        self.map = config_map
      
    #---------------------------------------------------------------------------   
    def configUpdate(self, name, current=None):
        pass
        # Update the DSP configuration
        # We don't deal with changing the output streaming at the moment (restart)
        if name in self.map:
            # Its something we are interested in so go get it
            item = utils.persist_get(self.persist, 'Profile', name)
            # and pass it in
            self.map[name](item)
        
#===============================================================================
# Receiver for the audio stream
class MicInI(AcornMicIn.MicIn):
    
    #---------------------------------------------------------------------------
    def __init__(self, dttsp, stream_out):
        self.dttsp = dttsp
        self.stream_out = stream_out
    
    #---------------------------------------------------------------------------   
    def data(self, samples, current=None):
        # do the conversion of samples here
        # Run one DSP cycle
        samples_as_double = asarray(samples, 'd')
        #print 'Input: ', samples_as_double[0][:20]
        real_out = self.dttsp.process(samples_as_double[0], samples_as_double[1])
        #print 'Output: ', real_out[0][:20]
        #sleep(0.02)
        #real_out_as_float = asarray(real_out, 'f')
        #real_out_as_float = real_out.astype('f')
        # Move the samples to the output stream
        
        self.stream_out.data(real_out.tolist())
        # Apply any updates we received
        #self.dsp_impl.apply_updates()
                
#===============================================================================
class DttSpTxService(Ice.Application):
    
    #---------------------------------------------------------------------------
    def __init__(self):       
        self.close = False
    
    #---------------------------------------------------------------------------
    # Run the app
    def run(self, args):
       
       #-----------------------------------------------------------------------
        # persist interface
        persist = ice_helpers.get_interface('DttSpTx Service', self.communicator, AcornPersist.PersistPrx, 'Persist.Proxy')
        # Get config
        frames = utils.persist_get(persist, 'Profile', 'audio.frames')
        rate = utils.persist_get(persist, 'Profile', 'audio.rate')
        
        #-----------------------------------------------------------------------
        # Event names used here
        publish_to_tx_updates = 'tx_update_events'
        publish_to_tx_display = 'tx_display_events'
        subscribe_to_tx_config = 'tx_config_events'
        subscribe_to_tx_dsp = 'tx_dsp_events'
        subscribe_to_lc = 'life_cycle_events'
  
        #-----------------------------------------------------------------------
        # This is the same for both publishers and subscribers
        # Get the IceStorm topic manager interface
        manager = ice_helpers.get_topic_manager('DttSpTx Service', self.communicator)
        
        # Retrieve the subscriber topic
        tx_dsp_topic = ice_helpers.get_topic('DttSpTx Service', manager, subscribe_to_tx_dsp)
        life_cycle_topic = ice_helpers.get_topic('DttSpTx Service', manager, subscribe_to_lc)
        tx_config_topic = ice_helpers.get_topic('DttSpTx Service', manager, subscribe_to_tx_config)
        
        # Retrieve the publisher topic
        tx_update_topic = ice_helpers.get_topic('DttSpTx Service', manager, publish_to_tx_updates)
        tx_display_topic = ice_helpers.get_topic('DttSpTx Service', manager, publish_to_tx_display)
  
        #-----------------------------------------------------------------------
        # Publisher side
        # Get the update topic's publisher object, and create an update proxy
        publisher_1 = tx_update_topic.getPublisher();
        publisher_1 = publisher_1.ice_oneway();        
        tx_update_publisher = AcornTxUpdate.TxUpdatePrx.uncheckedCast(publisher_1)
        
        # Get the display topic's publisher object, and create a display proxy
        publisher_2 = tx_display_topic.getPublisher();
        publisher_2 = publisher_2.ice_oneway();        
        tx_display_publisher = AcornTxDisplays.TxDisplaysPrx.uncheckedCast(publisher_2)
        
        #-----------------------------------------------------------------------
        # Initialise the Dsp as its needed for the adapter and requires the update publisher
        dttsp_impl = dttsp_tx.Dsp(persist, tx_update_publisher, tx_display_publisher, frames, rate)
        # Set up a map for updates
        config_map = {}
        
        #-----------------------------------------------------------------------
        # Subscriber side
        # Create an adapter for the subscriber dsp interface
        adapter_1 = self.communicator().createObjectAdapter("TxDsp.Subscriber")
        subId_1 = Ice.Identity()
        subId_1.name = Ice.generateUUID()
        # Add the update interface to the adapter
        subscriber_1 = adapter_1.add(TxDspI(dttsp_impl, self), subId_1)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('DttSpTx Service', tx_dsp_topic, subscriber_1, id)
        # Now we can receive callbacks   
        adapter_1.activate()
        
        adapter_2 = self.communicator().createObjectAdapter("LifeCycle.Subscriber")
        subId_2 = Ice.Identity()
        subId_2.name = Ice.generateUUID()        
        # Add the update interface
        subscriber_2 = adapter_2.add(LifeCycleI(dttsp_impl, self), subId_2)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('DttSpTx Service', life_cycle_topic, subscriber_2, id)
        # Now we can receive callbacks   
        adapter_2.activate()
        
        adapter_3 = self.communicator().createObjectAdapter("TxConfig.Subscriber")
        subId_3 = Ice.Identity()
        subId_3.name = Ice.generateUUID()        
        # Add the update interface
        subscriber_3 = adapter_3.add(TxConfigI(dttsp_impl, persist, config_map), subId_3)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('DttSpTx Service', tx_config_topic, subscriber_3, id)
        # Now we can receive callbacks   
        adapter_3.activate()
        
        #-----------------------------------------------------------------------
        # One way audio channel out
        # Set up the proxy
        # The stream can be split for remote HPSDR or single
        iq_out = ice_helpers.get_interface('DttSpTx Service', self.communicator, AcornIqOut.IqOutPrx, 'IqOut.Proxy')
        ozyStreamOneway = AcornIqOut.IqOutPrx.uncheckedCast(iq_out.ice_oneway())
        
        #-----------------------------------------------------------------------
        # One way audio channel in
        audio_adapter = self.communicator().createObjectAdapter("MicIn")
        audio_adapter.add(MicInI(dttsp_impl, iq_out), self.communicator().stringToIdentity("micin"))       
        # Now we can receive callbacks for audio 
        audio_adapter.activate()
        sleep(2)
        
        print 'Acorn-SDR (DttSpTx Service) running..'
        
        #-----------------------------------------------------------------------
        # Wait for closedown
        while not self.close:
            # Idle loop for doing any other work
            sleep(1)

        # Unsubscribe all subscribed objects.
        # Causes an error?
        #tx_dsp_topic.unsubscribe(subscriber_1)
        #life_cycle_topic.unsubscribe(subscriber_2)
        #tx_config_topic.unsubscribe(subscriber_3)
    
    #---------------------------------------------------------------------------
    # Terminate
    def terminate(self):
        self.close = True
        
#===============================================================================
#===============================================================================
# Start from the command line
if __name__ == "__main__":
   
    app = DttSpTxService()
    app.main(sys.argv, sys.argv[1])
  
    # Announce we are going 
    print 'Acorn-SDR  (DttSpTx Service) closing..'



